<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>performance</title>
    <ul>

    </ul>
</head>
<body>
<h2>测试with 和 直接用 代码进行判断的性能开销</h2>
<script id="js-temp" type="text/html">
    <div class="test">
        <ul v-for="item in list">
            <li class="item">{{item}}---- list.k2</li>
        </ul>
        <hr>
        <a href="#">test</a>
        <hr>
        <img src="{{img}}" alt="{{imagName}}">
        <div v-if="item.k1">
            v-if:{{item.k1}}
        </div>
    </div>
</script>
<script>
    /*
     * @author will.jiang
     * 日期 2017 10 31 15:18
     * 功能描述 强化模板引擎，
     * 使用width方式，方便获取内容
     *
     * @param {num} 参数1说明
     * @return {num} 返回值说明
     * */

    function render(temp, data) {
        var idx = 0, dList = [], rs;

        //用replace 方法来生成返回数据和埋点
        rs = temp.replace(/{{([^}]+)}}/g, function (match) {
            var cont = match.substring(2, match.length - 2);

            if (cont.indexOf('(') == -1 && cont.indexOf('?') == -1) {
                //不是方法
                //比如 name 返回  data.name||name 这样就可以在自身属性上取值或是从外部取值
                //当上方的设置会忽略掉自身属性为false的值
                //添加{{cont}} 只是用于标记找不到的key，这样可以在模板中比较方便的定位
                cont = '(typeof d.' + cont + ' ==="undefined"?"{{' + cont + '}}":d.' + cont + ')';
            }
            dList.push(cont);
            return '${' + (idx++) + '}'
        });

        //字符串数据转换为方法来执行。
        var list = (new Function('d', 'return [' + dList.join(',') + ']'))(data);
        //通过埋点来重新装载数据
        rs = rs.replace(/\${\d+}/g, function (match) {
            idx = match.substring(2, match.length - 1);
            return list[idx];
        });
        return rs;
    }
    function renderWith(temp, data) {
        var idx = 0, dList = [], rs;

        //用replace 方法来生成返回数据和埋点
        rs = temp.replace(/{{([^}]+)}}/g, function (match) {
            var cont = match.substring(2, match.length - 2);

            if (cont.indexOf('(') == -1 && cont.indexOf('?') == -1) {
                //不是方法
                //比如 name 返回  data.name||name 这样就可以在自身属性上取值或是从外部取值
                //当上方的设置会忽略掉自身属性为false的值
                //添加{{cont}} 只是用于标记找不到的key，这样可以在模板中比较方便的定位
                cont = '(typeof ' + cont + ' ==="undefined"?"{{' + cont + '}}":' + cont + ')';
            }
            dList.push(cont);
            return '${' + (idx++) + '}'
        });

        //字符串数据转换为方法来执行。
        var list = (new Function('d', 'with(d){ return [' + dList.join(',') + ']} '))(data);
        //通过埋点来重新装载数据
        rs = rs.replace(/\${\d+}/g, function (match) {
            idx = match.substring(2, match.length - 1);
            return list[idx];
        });
        return rs;
    }

    var max = 2000, i;

    /*---------------------开坑一个支持v-for语法的模板解析器----------------------------*/
    var _render = {
        //记
        directTagMap: {},

        /*
         * @author will.jiang
         * 日期 2017 11 01 14:23
         * 功能描述 根据模板标记标签，用于确定循环标签的影响范围.
         * 返回的模板会对有v-XX的指令进行范围影响确定，
         * 比如
         * <div>
         *     <p v-for="item in list">{{item}}</p>
         * </div>
         *  转换为
         * <div>
         *     <p-&-1 v-for="item in list">{{item}}</p-&-1>
         * </div>
         * 这样做是因为开始的标签往后面查找不一定是第一个闭合标签，因为中间可能出现标签嵌套
         * @param {str} 模板
         * @param {obj}  对象
         * @return {num} 返回值说明
         * */
        _matchTag: function (temp,data) {
            var directiveMap = {};
            var tagMap = {}, vReg = /v-([a-z]+)/;
            var rs = temp.replace(/<[^>]+>/g, function (matchStr) {
                var isNeedHack = false;
                var lastReg = /<([^ >]+).*>/, tag = matchStr.match(lastReg)[1];
                //统计
                var rs = $storeTag(tag).join("-&-");

                //查询当前标签是否具有 指令
                var directive = matchStr.match(vReg);
                if (directive) {
                    isNeedHack = true;
                    //记录下标签和对应的ID
                    directiveMap[rs] = directive[1];
                } else if (directiveMap[rs]) {
                    //还需要特别处理下结尾的标记
                    isNeedHack = true;
                    //添加下斜线
                    rs= '/'+rs;
                }
                //替换后返回
                return isNeedHack ? matchStr.replace(tag, rs) : matchStr;
            });

            //2.开始处理指令内容

            console.log(rs);
            this._renderDirective(rs,data,directiveMap);
            return rs;
            //会自动提升
            function $storeTag(tag) {
                var idx = 0;

                if (tag.indexOf("/") != -1) {
                    //开头的标签如 div a 等
                    tag = tag.substr(1);
                    idx = 1;
                }
                //存储数据
                var rs = tagMap[tag];
                if (rs == undefined) {
                    rs = tagMap[tag] = [0, 0];
                }
                rs[idx]++;
                //由于关闭的开关是第一个查找到的反而是第一层，因此需要用当前的值去减
                return [tag, idx == 0 ? rs[0] : rs[0] - rs[1] + 1]
            }
        },
        /*
        * @author will.jiang
        * 日期 2017 11 01 15:15
        * 功能描述 根据渲染的内容进行指令部分的循环处理
        * @param {num} 参数1说明
        * @param {num} 参数1说明
        * @param {num} {"ul-&-1":'for'}
        * @return {num} 返回值说明
        * */
        _renderDirective:function(temp,data,directiveMap){
            var list = Object.keys(directiveMap),self = this;
             list.forEach(function(k){
                self._getTemp(temp,k);

             });

            console.log(temp);
        },
        /*
        * @author will.jiang
        * 日期 2017 11 01 17:32
        * 功能描述 去查找一段内容
        * @param {num} 参数1说明
        * @return {num} 返回值说明
        * */
        _getTemp:function(temp,tag){
            //1.有可能有是单个标签
            var lastIndex = temp.indexOf('/'+tag),reg = '<'+tag+'([^>]+)>';
            if(lastIndex==-1){
                reg = new RegExp(reg);
            }else{
                //有开合标签，由于js中.是不匹配
                reg = new RegExp(reg+'[\\s\\S]+'+tag+'>');
            }
            temp.replace(reg,function(match){
                console.log("--------");
                console.log(match);
                return '{{'+tag+"}}"
            })
        },
        //解析模板
        _renderTemp:function(){

        },
        _vFor:function(temp,data){

        },
        _vIf:function(temp,data){

        },
        /*
         * @author will.jiang
         * 日期 2017 11 01 14:05
         * 功能描述 解析模板进行处理
         * 因为有可能有v-for内容
         * @param {num} 参数1说明
         * @return {num} 返回值说明
         * */
        render: function (temp, data) {
            if (temp.indexOf("v-for") != -1) {
                this._matchTag(temp,data);
            }
        }
    };
    //--------------------测试原始-------------------
    /*    console.time("render");
     for (i = 0; i<max; i++) {
     console.log(render("test{{name}} {{others}},<p>{{name}}></p>",{name:" will"}));
     }
     console.timeEnd("render");

     console.time("render width");
     for (i = 0; i<max; i++) {
     console.log(renderWith("test{{name}} {{others}},<p>{{name}}></p>",{name:" will"}));
     }
     console.timeEnd("render width");*/


    //1.统计标签
    var temp = document.getElementById("js-temp").innerHTML;


    _render.render(temp, {test: 1234})
</script>
</body>
</html>